package com.jcl.cloud.services.login.task;


import com.alibaba.fastjson.JSON;
import com.jcl.cloud.services.login.exception.CookieErrException;
import com.jcl.cloud.services.login.exception.LoginErrException;
import com.jcl.cloud.services.login.exception.OutTimesException;
import com.jcl.cloud.services.login.exception.OverTimeException;
import com.jcl.cloud.services.login.factory.WebDriverPool;
import com.jcl.cloud.services.login.model.InputModel;
import com.jcl.cloud.services.login.model.LoginModel;
import com.jcl.cloud.services.login.model.TaskStatusModel;
import com.jcl.cloud.services.login.service.RedisService;
import com.jcl.cloud.services.login.util.ImageUtil;
import com.jcl.cloud.services.login.util.SerializeUtils;
import com.jcl.cloud.services.login.util.StringUtil;
import com.jcl.cloud.services.login.util.yzm.DaMaUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
public class ZlzpLoginTaskHandler implements LoginTaskHandler{
    private static Logger logger = LoggerFactory.getLogger(ZlzpLoginTaskHandler.class);


    @Autowired
    private RedisService redisService;

    @Override
    public void execute(LoginModel loginModel) {
        try {
            reportStatus(1,loginModel);
            boolean isInputSucc = doInput(loginModel);
            boolean isClickSucc = false;
            if (isInputSucc) {
               isClickSucc =  clickLoginBtn(loginModel);
            }

            reportStatus(2,loginModel);
            boolean isVCodeSucc = false;
            if(isClickSucc){
                isVCodeSucc = handleVCode(loginModel);
            }

            if (isVCodeSucc) {
                //报告状态，验证码验证成功,验证失败会抛异常
                reportStatus(3,loginModel);
            }

            reportStatus(4,loginModel);
            boolean isLoginSucc = handleLogin(loginModel);
            if (isLoginSucc) {
                //登录成功
                reportStatus(5,loginModel);
            }

            //上传cookies
            handleCookies(loginModel);
            reportStatus(6,loginModel);

            //登录成功,将任务重新放回队列
            this.redisService.lpush("unicrawler:tasks:all",loginModel.getCrawlerId());
        } catch (LoginErrException loee) {
            logger.error(String.format("任务号：%s    执行发生LoginErrException异常：%s", loginModel.getCrawlerId(), loee.getMessage()), loee);
            loginModel.setLoginErrMsg(loee.getMessage());
            reportStatus(7,loginModel);
        } catch (OutTimesException orte) {
            logger.error(String.format("任务号：%s    执行发生OverTimesException异常：%s", loginModel.getCrawlerId(), orte.getMessage()), orte);

            loginModel.setLoginErrMsg(orte.getMessage());
            reportStatus(8,loginModel);
        } catch (CookieErrException cee) {
            logger.error(String.format("任务号：%s    执行发生CookieErrException异常：%s", loginModel.getCrawlerId(), cee.getMessage()), cee);

            loginModel.setLoginErrMsg(cee.getMessage());
            reportStatus(9,loginModel);
        } catch (Exception e) {
            logger.error(String.format("任务号：%s    执行发生Exception异常：%s", loginModel.getCrawlerId(), e.getMessage()), e);

            loginModel.setLoginErrMsg("系统异常");
            reportStatus(10,loginModel);
        } finally {
            try {
                //CustomJedisPool.returnResource(jedis);
                WebDriverPool.getInstance().returnObject("FIREFOX", loginModel.getDriver());
            } catch (Exception e) {
                logger.error(String.format("任务号：%s    资源归还发生异常：%s", loginModel.getCrawlerId(), e.getMessage()), e);
            }
        }

    }




    private boolean doInput(LoginModel loginModel) throws Exception {
        WebDriver driver = loginModel.getDriver();


        String taskId = loginModel.getCrawlerId();
        driver.get("https://passport.zhaopin.com/org/login");

        Thread.sleep(2000);

        File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_zlzp_输入用户信息前.png"));



        boolean flag = false;


        List list =  this.redisService.hmget("unicrawler:task:info:"+loginModel.getCrawlerId(),"account","userName","passWord");

        JavascriptExecutor executor = (JavascriptExecutor) driver;
        executor.executeScript("document.getElementById(\"loginName\").value="+ "\"" + list.get(1) + "\"");
        executor.executeScript("document.getElementById(\"password\").value="+ "\"" + list.get(2) + "\"");
        flag = true;
        logger.info(String.format("任务号：%s   输入用户信息成功", taskId));

        return flag;
    }


    private boolean clickLoginBtn(LoginModel loginModel) throws Exception {
        boolean flag = false;

        String taskId = null;
        try {
            Thread.sleep(2000);
            taskId = loginModel.getCrawlerId();

            WebDriver driver = loginModel.getDriver();

            logger.info("页面信息为：" + driver.getPageSource());

            File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_zlzp_点击登录按钮前.png"));


            //点击 获取验证码
           // WebElement checkCodeCapt = driver.findElement(By.id("checkCodeCapt"));//checkCodeCapt

            JavascriptExecutor executor = (JavascriptExecutor) driver;
            executor.executeScript("document.getElementById(\"loginbutton\").click()");


            Thread.sleep(5000);
            scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_zlzp_点击登录按钮后.png"));
            //checkCodeCapt.click();
            flag =true;
        } catch (ElementNotInteractableException e) {
            logger.error("点击验证码按钮发生 ElementNotInteractableException 异常：",e);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

        logger.info(String.format("任务号：%s   点击验证码按钮", taskId));

        return flag;
    }

    private byte[] getVCodeImage(LoginModel loginModel) throws Exception {
        Thread.sleep(2000);
        int times = 0;
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();

        WebElement geetest_widget = null;
        do {
            times++;
            logger.info("获取验证码时，查找geetest_widget的次数为：" + times);
            try {
                geetest_widget = ((FirefoxDriver) driver).findElementByClassName("geetest_widget");
                break;
            } catch (Exception e) {

                //获取异常时的截图，保存现场
                File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
                FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_" + times +"_zlzp_查找geetest_widget元素异常后的截图.png"));


                logger.error("获取验证码截图时，查找geetest_widget发生异常：" + e.getMessage(),e);
                Thread.sleep(3000);
                //点击登录按钮
                this.clickLoginBtn(loginModel);
            }

            if(times > 10){
                break;
            }

        } while (true);
        File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

        Point p = geetest_widget.getLocation();
        int width = geetest_widget.getSize().getWidth();
        int higth = geetest_widget.getSize().getHeight();
        BufferedImage img = ImageIO.read(scrFile);
        BufferedImage dest = img.getSubimage(p.getX(), p.getY(), width, higth);

        ImageIO.write(dest, "png", scrFile);
        FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/"+taskId+"_zlzp的登录验证码.png"));


        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(dest, "png", out);

        byte[] imgCode = ImageUtil.compressPic(out.toByteArray(), 0.5f);

        return imgCode;
    }


    private boolean handleVCode(LoginModel loginModel) throws Exception {
        boolean flag = false;
        int times = 0;
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();
        do {
            times++;

            byte[] code = getVCodeImage(loginModel);

            logger.info(String.format("任务号：%s 验证码图片大小：%s", taskId, (code.length * 4 / 1024) + "K"));
            String daMaResult = DaMaUtil.recognizeImage(code);
            logger.info(String.format("任务号：%s    打码平台获取到的验证码结果：%s", taskId, daMaResult));
            //处理验证码
            String pointVal = StringUtil.getStrByRgex(daMaResult, "<Result>(.*?)</Result>");

            String daMaId = StringUtil.getStrByRgex(daMaResult, "<Id>(.*?)</Id>");
            if (StringUtils.isNotBlank(pointVal)) {
                String[] pArr = pointVal.replaceAll(" ", "").split("\\.");
                String[] pBak = {};
                //通过坐标点击汉字
                /*WebElement captcha_container = ((FirefoxDriver) driver).findElementByClassName("captcha-container");
                int img_width = captcha_container.getSize().getWidth();
                int img_higth = captcha_container.getSize().getHeight();*/


                Thread.sleep(2000);
                WebElement geetest_widget = ((FirefoxDriver) driver).findElementByClassName("geetest_widget");
                int img_width = geetest_widget.getSize().getWidth();
                int img_higth = geetest_widget.getSize().getHeight();
                Actions action = new Actions(driver);


                for (String pStr : pArr) {
                    pBak = pStr.split(",");               //Integer.parseInt(pBak[0]) - img_width / 2, Integer.parseInt(pBak[1]) - img_higth / 2
                    action.moveToElement(geetest_widget, Integer.parseInt(pBak[0]) - img_width / 2, Integer.parseInt(pBak[1]) - img_higth / 2 ).click().perform();
                    Thread.sleep(100);
                }


                File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
                FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_zlzp_点击验证码坐标后.png"));






                driver.findElement(By.className("geetest_commit")).click();

                Thread.sleep(6000);

                scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
                FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/" + taskId + "_zlzp_点击验证码确认按钮后.png"));

                //查看验证码是否通过校验
                boolean isVCodeSucc = checkVCode(loginModel);
                if (isVCodeSucc) {
                    logger.info(String.format("任务号：%s    验证码验证通过", taskId));
                    flag = true;
                    break;
                } else {
                    logger.error(String.format("任务号：%s    验证码验证失败，重试次数times：%s", taskId, times));

                    if (times > 10) {
                        throw new OutTimesException("验证码模拟验证重试shi次失败");
                    }
                    //上报打码平台验证码错误
                    String result = DaMaUtil.reportErr(daMaId);
                    logger.info(String.format("任务号：%s  打码Id：%s   上报打码坐标错误返回的结果：%s", taskId, daMaId, result));
                    continue;
                }
            } else {
                logger.info(String.format("任务号：%s   打码平台返回异常，重新打码", taskId));
                Thread.sleep(1000);

                if (times > 3) {
                    throw new OutTimesException("验证码打码重试三次失败");
                }
                continue;
            }

        } while (true);

        return flag;
    }


    private boolean handleLogin(LoginModel loginModel) throws Exception {
        boolean flag = false;
        String source = "";
        int times = 0;
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();

        //点击登录
        //driver.findElement(By.id("loginbutton")).click();
        do {
            times++;

            source = driver.getPageSource();
            String rgex = "<div id=\"alertMessage\" style=\"color: red; width: 303px; margin-left: 16px; opacity: 1; right: 0px; display: block;\" class=\"error\"(.*?)</div>";
            String tipMsg = StringUtil.getStrByRgex(source, rgex);
            if (StringUtils.isNotBlank(tipMsg)) {
                logger.info(String.format("任务号：%s   用户名或密码错误的提示信息：%s", taskId, tipMsg));
                flag = false;

                loginModel.setLoginErrMsg(tipMsg);
                break;
            }

            if (source.contains("智联账号验证")) {
                logger.info(String.format("任务号：%s   登录系统需要扫二维码验证", taskId));
                Thread.sleep(3000);
                String QRCode = getQRCodeImage(loginModel);

                this.redisService.hset("unicrawler:task:info:"+loginModel.getCrawlerId(), "QRCode", QRCode);

                loginModel.setQRCode(new String (QRCode));
                reportStatus(9,loginModel);
                logger.info(String.format("任务号：%s   二维码上报成功,等待用户扫码", taskId));
                long startTime = new Date().getTime();
                long endTime = 0;
                /*do {
                    String result = getUserInput(loginModel);
                    if (StringUtils.isNotBlank(result)) {
                        logger.info(String.format("任务号：%s  获取到用户输入结果：%s", taskId, result));
                        Thread.sleep(1000);
                        break;
                    } else {
                        logger.info(String.format("任务号：%s   未获取到用户输入信息", taskId));
                        Thread.sleep(1000);
                        endTime = new Date().getTime();
                        int wait_time = 8 * 60;//默认等待3分钟
                        if ((int) ((endTime - startTime) / 1000) > wait_time) {
                            logger.error(String.format("任务号：%s   等待用户扫码超时", taskId));
                            throw new OverTimeException("等待用户扫码超时");
                        }

                        continue;
                    }
                } while (true);*/


                //判断页面是否跳转
                int retry = 0;
                do {
                    retry++;
                    source = driver.getPageSource();
                    if (source.contains("查看简历")) {
                        logger.info(String.format("任务号：%s   用户扫码验证后系统登录成功", taskId));
                        flag = true;
                        break;
                    } else {
                        logger.info(String.format("任务号：%s   二维码上报成功,等待用户扫码中....", taskId));
                        Thread.sleep(2000);
                        logger.info(String.format("任务号：%s   用户扫码验证后页面未跳转，继续等待", taskId));
                        if (retry > 300) {
                            logger.info(String.format("任务号：%s   用户扫码验证后页面跳转发生异常,页面代码：%s", taskId, source));
                            throw new OverTimeException("用户扫码验证后页面跳转发生异常");
                        }
                        continue;
                    }
                } while (true);


            } else if (source.contains("查看简历")) {
                logger.info(String.format("任务号：%s  系统登录成功", taskId));
                flag = true;
                break;
            } else {
                logger.info(String.format("任务号：%s  等待次数times:%s  页面未跳转，系统等待中...", taskId, times));
                Thread.sleep(1000);
            }

            if (times > 6) {
                throw new OutTimesException("点击登录按钮后，页面等待发生超时");
            }
        } while (true);
        return flag;
    }

    private String getQRCodeImage(LoginModel loginModel) throws Exception {
        String base64Image = "";
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();
        try {
            //WebElement iframe = driver.findElement(By.tagName("iframe"));

            WebElement code_img = driver.findElement(By.className("code-img"));
            //driver.switchTo().frame(iframe);
           // WebElement qrCodeEle = driver.findElement(By.className("safe-login-box"));
            //logger.info("****************iframe = " + iframe.getText() + "*****************");



            File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);




            Point p = code_img.getLocation();
            int width = code_img.getSize().getWidth();
            int higth = code_img.getSize().getHeight();
            BufferedImage img = ImageIO.read(scrFile);
            BufferedImage dest = img.getSubimage(p.getX(), p.getY(), width, higth);
            ImageIO.write(dest, "png", scrFile);

            //FileUtils.copyFile(scrFile, new File("/app/jcl-crawler/images/"+taskId+".png"));

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(dest, "png", out);
            byte[] imgCode = out.toByteArray();






            BASE64Encoder encoder = new BASE64Encoder();

            String base64Str = encoder.encode(imgCode);

            base64Image = "data:image/png;base64," + base64Str;

            logger.info(String.format("任务号：%s   getQRCodeImage成功，bigImage=%s", taskId, base64Image.replaceAll(" ","")));
        } catch (Exception e) {
            logger.error(String.format("任务号：%s   getQRCodeImage发生异常：%s", taskId, e.getMessage()), e);
            throw e;
        }

        return base64Image.replaceAll(" ","");


    }


    private void handleCookies(LoginModel loginModel) throws Exception {
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();

        HttpClientContext context = null;
        BasicCookieStore cookieStore = null;
        try {

            cookieStore = new BasicCookieStore();
            context = HttpClientContext.create();
            Set<Cookie> cookieSet = driver.manage().getCookies();

            for (Cookie c : cookieSet) {
                BasicClientCookie cookie = new BasicClientCookie(c.getName(), c.getValue());
                cookie.setDomain(c.getDomain());
                cookie.setPath(c.getPath());
                cookie.setExpiryDate(c.getExpiry());
                cookieStore.addCookie(cookie);
            }

        } catch (Exception e) {
            logger.error(String.format("任务号：%s    获取登录cookie发生异常：%s", taskId, e.getMessage()), e);
            throw new CookieErrException("获取登录Cookie发生异常");
        }


        String cookieStoreStr = SerializeUtils.serializeToStr(cookieStore);
        logger.info(String.format("任务号：%s   上传的登录cookie信息：%s", taskId, cookieStoreStr));
        this.redisService.hset("unicrawler:task:info:" + taskId, "cookieStore", cookieStoreStr);
    }


    private boolean checkVCode(LoginModel loginModel) throws Exception {
        boolean flag = false;
        WebDriver driver = loginModel.getDriver();
        String taskId = loginModel.getCrawlerId();

        /*String conditionA = "value=\"点击完成验证\"";
        String conditionB = "value=\"正在验证\"";
        String conditionC = "value=\"验证通过\"";
        String conditionD = "value=\"验证失败\"";

        String source = driver.getPageSource();
        if (source.contains(conditionA)) {
            logger.info(String.format("任务号：%s  上一步模拟点击验证码发生异常", taskId));
        } else if (source.contains(conditionB)) {
            logger.info(String.format("任务号：%s  模拟点击验证码未完成，休息一秒", taskId));
            Thread.sleep(1000);
            source = driver.getPageSource();
            if (source.contains(conditionC)) {
                //验证通过
                flag = true;
                logger.info(String.format("任务号：%s  休息一秒后验证模拟点击验证码成功", taskId));
            } else if (source.contains(conditionD)) {
                //验证失败
                flag = false;
                logger.info(String.format("任务号：%s  休息一秒后验证模拟点击验证码失败", taskId));
            }
        } else if (source.contains(conditionC)) {
            //验证通过
            flag = true;
            logger.info(String.format("任务号：%s  模拟点击验证码成功", taskId));
        } else if (source.contains(conditionD)) {
            //验证失败
            flag = false;
            logger.info(String.format("任务号：%s  模拟点击验证码失败", taskId));
        }*/

        //geetest_widget

        Thread.sleep(2000);
        String source = driver.getPageSource();

        if(source.contains("class=\"geetest_widget\"")){
            //验证失败
            flag = false;
            logger.info(String.format("任务号：%s  模拟点击验证码失败", taskId));
        }else {
            //验证通过
            flag = true;
            logger.info(String.format("任务号：%s  模拟点击验证码成功", taskId));
        }

        return flag;
    }


    private void reportStatus(int step,LoginModel loginModel) {

        String taskId = loginModel.getCrawlerId();
        String QRCode = loginModel.getQRCode();
        String loginErrMsg = loginModel.getLoginErrMsg();
        TaskStatusModel tsm = new TaskStatusModel();
        InputModel input = new InputModel();
        if (step == 1) {
            tsm.setDescription("登录信息准备中");
            tsm.setFinished(false);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DOING");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }

        if (step == 2) {
            tsm.setDescription("验证码验证中");
            tsm.setFinished(false);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DOING");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }


        if (step == 3) {
            tsm.setDescription("验证码验证成功");
            tsm.setFinished(false);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DOING");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }


        if (step == 4) {
            tsm.setDescription("登录中");
            tsm.setFinished(false);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DOING");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }


        if (step == 5) {
            tsm.setDescription("登录成功");
            tsm.setFinished(true);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DONE_SUCC");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }

        if (step == 6) {
            tsm.setDescription("登录cookie获取成功");
            tsm.setFinished(true);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DONE_SUCC");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }


        if (step == 7) {
            tsm.setDescription(loginErrMsg);
            tsm.setFinished(true);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DONE_FAIL");
            tsm.setTask_id(taskId);
            tsm.setError_code("401");//其他错误
        }

        if (step == 8) {
            tsm.setDescription(loginErrMsg);
            tsm.setFinished(true);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DONE_FAIL");
            tsm.setTask_id(taskId);
            tsm.setError_code("402");//系统异常
        }

        if (step == 9) {
            input.setType("img");
            input.setValue(QRCode);

            tsm.setDescription("等待用户扫码验证中");
            tsm.setFinished(false);
            tsm.setInput(input);
            tsm.setPhase("LOGIN");
            tsm.setPhase_status("DOING");
            tsm.setTask_id(taskId);
            tsm.setError_code("");
        }

        String msg = JSON.toJSONString(tsm);
        logger.info(String.format("任务号：%s    上传的状态msg：%s", taskId, msg));
        this.redisService.hset("unicrawler:task:info:"+loginModel.getCrawlerId(), "status", msg);
    }


    private String getUserInput(LoginModel loginModel) {

        String result = "";
        String taskId = loginModel.getCrawlerId();

        try {
            result = this.redisService.hget("unicrawler:task:info:"+loginModel.getCrawlerId(),"input");
        } catch (Exception e) {
            logger.info(String.format("任务号：%s    获取用户输入信息发生异常：%s", taskId, e.getMessage()), e);
        }
        return result;
    }



}
